use crate::{
    ast::{types::MapType, Type},
    error::{CompilerError, Result},
    llvm_backend::LLVMBackend,
};
use inkwell::{
    types::BasicType,
    values::{BasicValue, BasicValueEnum},
    AddressSpace,
};

impl<'ctx> LLVMBackend<'ctx> {
    /// Generate map.len() method
    /// Returns the number of key-value pairs in the map
    pub(in crate::llvm_backend) fn generate_map_len_method(
        &mut self,
        var_name: &str,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the map variable pointer from variable_map
        let map_ptr = self
            .variable_map
            .get(var_name)
            .ok_or_else(|| {
                CompilerError::internal(format!("Undefined map variable: {}", var_name))
            })?
            .into_pointer_value();

        // Get the map type
        let map_type = self.variable_type_map.get(var_name).ok_or_else(|| {
            CompilerError::internal(format!("Undefined map type for variable: {}", var_name))
        })?;

        let (key_type, value_type) = if let Type::Map(MapType {
            key_type,
            value_type,
            ..
        }) = map_type
        {
            (*key_type.clone(), *value_type.clone())
        } else {
            return Err(CompilerError::internal("len() requires a map variable"));
        };

        // Get the key and value LLVM types
        let key_llvm_type = self.nrc_type_to_llvm_type(&Some(key_type))?;
        let value_llvm_type = self.nrc_type_to_llvm_type(&Some(value_type))?;

        // Create map struct type: {keys: *K, values: *V, len: i64, cap: i64}
        let keys_ptr_type = key_llvm_type.ptr_type(AddressSpace::default());
        let values_ptr_type = value_llvm_type.ptr_type(AddressSpace::default());
        let map_struct_type = self.context.struct_type(
            &[
                keys_ptr_type.into(),
                values_ptr_type.into(),
                self.context.i64_type().into(),
                self.context.i64_type().into(),
            ],
            false,
        );

        // Load current map
        let current_map = self
            .builder
            .build_load(map_struct_type, map_ptr, "current_map")?;

        if !current_map.is_struct_value() {
            return Err(CompilerError::internal("Map value is not a struct"));
        }

        let map_struct = current_map.into_struct_value();

        // Extract len field from the map struct (field index 2)
        let len = self
            .builder
            .build_extract_value(map_struct, 2, "map_len")?
            .into_int_value();

        Ok(len.as_basic_value_enum())
    }
}
